home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Blender 2.49b / blender-2.49b-windows.exe / $_4_ / .blender / scripts / bpymodules / dxfLibrary.py < prev    next >
Text File  |  2009-08-31  |  29KB  |  880 lines

  1. #dxfLibrary.py : provides functions for generating DXF files
  2. # --------------------------------------------------------------------------
  3. __version__ = "v1.33 - 2009.06.16"
  4. __author__ = "Stani Michiels(Stani), Remigiusz Fiedler(migius)"
  5. __license__ = "GPL"
  6. __url__ = "http://wiki.blender.org/index.php/Scripts/Manual/Export/autodesk_dxf"
  7. __bpydoc__ ="""The library to export geometry data to DXF format r12 version.
  8.  
  9. Copyright %s
  10. Version %s
  11. License %s
  12. Homepage %s
  13.  
  14. See the homepage for documentation.
  15. Dedicated thread on BlenderArtists: http://blenderartists.org/forum/showthread.php?t=136439
  16.  
  17. IDEAs:
  18. -
  19.  
  20. TODO:
  21. - add support for DXFr14 (needs extended file header)
  22. - add support for SPLINEs (possible first in DXFr14 version)
  23. - add user preset for floating point precision (3-16?)
  24.  
  25. History
  26. v1.33 - 2009.06.16 by migius
  27.  - modif _point(): converts all coords to floats
  28.  - modif LineType class: implement elements
  29.  - added VPORT class, incl. defaults
  30.  - fix Insert class
  31. v1.32 - 2009.06.06 by migius
  32.  - modif Style class: changed defaults to widthFactor=1.0, obliqueAngle=0.0
  33.  - modif Text class: alignment parameter reactivated
  34. v1.31 - 2009.06.02 by migius
  35.  - modif _Entity class: added paperspace,elevation
  36. v1.30 - 2009.05.28 by migius
  37.  - bugfix 3dPOLYLINE/POLYFACE: VERTEX needs x,y,z coordinates, index starts with 1 not 0
  38. v1.29 - 2008.12.28 by Yorik
  39.  - modif POLYLINE to support bulge segments
  40. v1.28 - 2008.12.13 by Steeve/BlenderArtists
  41.  - bugfix for EXTMIN/EXTMAX to suit Cycas-CAD
  42. v1.27 - 2008.10.07 by migius
  43.  - beautifying output code: keys whitespace prefix
  44.  - refactoring DXF-strings format: NewLine moved to the end of
  45. v1.26 - 2008.10.05 by migius
  46.  - modif POLYLINE to support POLYFACE
  47. v1.25 - 2008.09.28 by migius
  48.  - modif FACE class for r12
  49. v1.24 - 2008.09.27 by migius
  50.  - modif POLYLINE class for r12
  51.  - changing output format from r9 to r12(AC1009)
  52. v1.1 (20/6/2005) by www.stani.be/python/sdxf
  53.  - Python library to generate dxf drawings
  54. ______________________________________________________________
  55. """ % (__author__,__version__,__license__,__url__)
  56.  
  57. # --------------------------------------------------------------------------
  58. # DXF Library: copyright (C) 2005 by Stani Michiels (AKA Stani)
  59. #                       2008/2009 modif by Remigiusz Fiedler (AKA migius)
  60. # --------------------------------------------------------------------------
  61. # ***** BEGIN GPL LICENSE BLOCK *****
  62. #
  63. # This program is free software; you can redistribute it and/or
  64. # modify it under the terms of the GNU General Public License
  65. # as published by the Free Software Foundation; either version 2
  66. # of the License, or (at your option) any later version.
  67. #
  68. # This program is distributed in the hope that it will be useful,
  69. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  70. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  71. # GNU General Public License for more details.
  72. #
  73. # You should have received a copy of the GNU General Public License
  74. # along with this program; if not, write to the Free Software Foundation,
  75. # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  76. #
  77. # ***** END GPL LICENCE BLOCK *****
  78.  
  79.  
  80. #import Blender
  81. #from Blender import Mathutils, Window, Scene, sys, Draw
  82. #import BPyMessages
  83.  
  84. try:
  85.     import copy
  86.     #from struct import pack
  87. except:
  88.     copy = None
  89.  
  90. ####1) Private (only for developpers)
  91. _HEADER_POINTS=['insbase','extmin','extmax']
  92.  
  93. #---helper functions-----------------------------------
  94. def _point(x,index=0):
  95.     """Convert tuple to a dxf point"""
  96.     #print 'deb: _point=', x #-------------
  97.     return '\n'.join([' %s\n%s'%((i+1)*10+index,float(x[i])) for i in range(len(x))])
  98.  
  99. def _points(plist):
  100.     """Convert a list of tuples to dxf points"""
  101.     out = '\n'.join([_point(plist[i],i)for i in range(len(plist))])
  102.     return out
  103.  
  104. #---base classes----------------------------------------
  105. class _Call:
  106.     """Makes a callable class."""
  107.     def copy(self):
  108.         """Returns a copy."""
  109.         return copy.deepcopy(self)
  110.  
  111.     def __call__(self,**attrs):
  112.         """Returns a copy with modified attributes."""
  113.         copied=self.copy()
  114.         for attr in attrs:setattr(copied,attr,attrs[attr])
  115.         return copied
  116.  
  117. #-------------------------------------------------------
  118. class _Entity(_Call):
  119.     """Base class for _common group codes for entities."""
  120.     def __init__(self,paperspace=None,color=None,layer='0',
  121.                  lineType=None,lineTypeScale=None,lineWeight=None,
  122.                  extrusion=None,elevation=None,thickness=None,
  123.                  parent=None):
  124.         """None values will be omitted."""
  125.         self.paperspace      = paperspace
  126.         self.color          = color
  127.         self.layer          = layer
  128.         self.lineType       = lineType
  129.         self.lineTypeScale  = lineTypeScale
  130.         self.lineWeight     = lineWeight
  131.         self.extrusion      = extrusion
  132.         self.elevation      = elevation
  133.         self.thickness      = thickness
  134.         #self.visible      = visible
  135.         self.parent         = parent
  136.  
  137.     def _common(self):
  138.         """Return common group codes as a string."""
  139.         if self.parent:parent=self.parent
  140.         else:parent=self
  141.         result =''
  142.         if parent.paperspace==1: result+='  67\n1\n'
  143.         if parent.layer!=None: result+='  8\n%s\n'%parent.layer
  144.         if parent.color!=None: result+=' 62\n%s\n'%parent.color
  145.         if parent.lineType!=None: result+='  6\n%s\n'%parent.lineType
  146.         # TODO: if parent.lineWeight!=None: result+='370\n%s\n'%parent.lineWeight
  147.         # TODO: if parent.visible!=None: result+='60\n%s\n'%parent.visible
  148.         if parent.lineTypeScale!=None: result+=' 48\n%s\n'%parent.lineTypeScale
  149.         if parent.elevation!=None: result+=' 38\n%s\n'%parent.elevation
  150.         if parent.thickness!=None: result+=' 39\n%s\n'%parent.thickness
  151.         if parent.extrusion!=None: result+='%s\n'%_point(parent.extrusion,200)
  152.         return result
  153.  
  154. #--------------------------
  155. class _Entities:
  156.     """Base class to deal with composed objects."""
  157.     def __dxf__(self):
  158.         return []
  159.  
  160.     def __str__(self):
  161.         return ''.join([str(x) for x in self.__dxf__()])
  162.  
  163. #--------------------------
  164. class _Collection(_Call):
  165.     """Base class to expose entities methods to main object."""
  166.     def __init__(self,entities=[]):
  167.         self.entities=copy.copy(entities)
  168.         #link entities methods to drawing
  169.         for attr in dir(self.entities):
  170.             if attr[0]!='_':
  171.                 attrObject=getattr(self.entities,attr)
  172.                 if callable(attrObject):
  173.                     setattr(self,attr,attrObject)
  174.  
  175. ####2) Constants
  176. #---color values
  177. BYBLOCK=0
  178. BYLAYER=256
  179.  
  180. #---block-type flags (bit coded values, may be combined):
  181. ANONYMOUS =1  # This is an anonymous block generated by hatching, associative dimensioning, other internal operations, or an application
  182. NON_CONSTANT_ATTRIBUTES =2  # This block has non-constant attribute definitions (this bit is not set if the block has any attribute definitions that are constant, or has no attribute definitions at all)
  183. XREF =4  # This block is an external reference (xref)
  184. XREF_OVERLAY =8  # This block is an xref overlay
  185. EXTERNAL =16 # This block is externally dependent
  186. RESOLVED =32 # This is a resolved external reference, or dependent of an external reference (ignored on input)
  187. REFERENCED =64 # This definition is a referenced external reference (ignored on input)
  188.  
  189. #---mtext flags
  190. #attachment point
  191. TOP_LEFT = 1
  192. TOP_CENTER = 2
  193. TOP_RIGHT = 3
  194. MIDDLE_LEFT = 4
  195. MIDDLE_CENTER = 5
  196. MIDDLE_RIGHT    = 6
  197. BOTTOM_LEFT = 7
  198. BOTTOM_CENTER = 8
  199. BOTTOM_RIGHT = 9
  200. #drawing direction
  201. LEFT_RIGHT = 1
  202. TOP_BOTTOM = 3
  203. BY_STYLE = 5 #the flow direction is inherited from the associated text style
  204. #line spacing style (optional):
  205. AT_LEAST = 1 #taller characters will override
  206. EXACT = 2 #taller characters will not override
  207.  
  208. #---polyline flags
  209. CLOSED =1      # This is a closed polyline (or a polygon mesh closed in the M direction)
  210. CURVE_FIT =2      # Curve-fit vertices have been added
  211. SPLINE_FIT =4      # Spline-fit vertices have been added
  212. POLYLINE_3D =8      # This is a 3D polyline
  213. POLYGON_MESH =16     # This is a 3D polygon mesh
  214. CLOSED_N =32     # The polygon mesh is closed in the N direction
  215. POLYFACE_MESH =64     # The polyline is a polyface mesh
  216. CONTINOUS_LINETYPE_PATTERN =128    # The linetype pattern is generated continuously around the vertices of this polyline
  217.  
  218. #---text flags
  219. #horizontal
  220. LEFT = 0
  221. CENTER = 1
  222. RIGHT = 2
  223. ALIGNED = 3 #if vertical alignment = 0
  224. MIDDLE = 4 #if vertical alignment = 0
  225. FIT = 5 #if vertical alignment = 0
  226. #vertical
  227. BASELINE = 0
  228. BOTTOM    = 1
  229. MIDDLE = 2
  230. TOP = 3
  231.  
  232. ####3) Classes
  233. #---entitities -----------------------------------------------
  234. #--------------------------
  235. class Arc(_Entity):
  236.     """Arc, angles in degrees."""
  237.     def __init__(self,center=(0,0,0),radius=1,
  238.                  startAngle=0.0,endAngle=90,**common):
  239.         """Angles in degrees."""
  240.         _Entity.__init__(self,**common)
  241.         self.center=center
  242.         self.radius=radius
  243.         self.startAngle=startAngle
  244.         self.endAngle=endAngle
  245.     def __str__(self):
  246.         return '  0\nARC\n%s%s\n 40\n%s\n 50\n%s\n 51\n%s\n'%\
  247.                (self._common(),_point(self.center),
  248.                 self.radius,self.startAngle,self.endAngle)
  249.  
  250. #-----------------------------------------------
  251. class Circle(_Entity):
  252.     """Circle"""
  253.     def __init__(self,center=(0,0,0),radius=1,**common):
  254.         _Entity.__init__(self,**common)
  255.         self.center=center
  256.         self.radius=radius
  257.     def __str__(self):
  258.         return '  0\nCIRCLE\n%s%s\n 40\n%s\n'%\
  259.                (self._common(),_point(self.center),self.radius)
  260.  
  261. #-----------------------------------------------
  262. class Face(_Entity):
  263.     """3dface"""
  264.     def __init__(self,points,**common):
  265.         _Entity.__init__(self,**common)
  266.         while len(points)<4: #fix for r12 format
  267.             points.append(points[-1])
  268.         self.points=points
  269.         
  270.     def __str__(self):
  271.         out = '  0\n3DFACE\n%s%s\n' %(self._common(),_points(self.points))
  272.         #print 'deb:out=', out #-------------------
  273.         return out
  274.  
  275. #-----------------------------------------------
  276. class Insert(_Entity):
  277.     """Block instance."""
  278.     def __init__(self,name,point=(0,0,0),
  279.                  xscale=None,yscale=None,zscale=None,
  280.                  cols=None,colspacing=None,rows=None,rowspacing=None,
  281.                  rotation=None,
  282.                  **common):
  283.         _Entity.__init__(self,**common)
  284.         self.name=name
  285.         self.point=point
  286.         self.xscale=xscale
  287.         self.yscale=yscale
  288.         self.zscale=zscale
  289.         self.cols=cols
  290.         self.colspacing=colspacing
  291.         self.rows=rows
  292.         self.rowspacing=rowspacing
  293.         self.rotation=rotation
  294.  
  295.     def __str__(self):
  296.         result='  0\nINSERT\n  2\n%s\n%s%s\n'%\
  297.                 (self.name,self._common(),_point(self.point))
  298.         if self.xscale!=None:result+=' 41\n%s\n'%self.xscale
  299.         if self.yscale!=None:result+=' 42\n%s\n'%self.yscale
  300.         if self.zscale!=None:result+=' 43\n%s\n'%self.zscale
  301.         if self.rotation:result+=' 50\n%s\n'%self.rotation
  302.         if self.cols!=None:result+=' 70\n%s\n'%self.cols
  303.         if self.colspacing!=None:result+=' 44\n%s\n'%self.colspacing
  304.         if self.rows!=None:result+=' 71\n%s\n'%self.rows
  305.         if self.rowspacing!=None:result+=' 45\n%s\n'%self.rowspacing
  306.         return result
  307.  
  308. #-----------------------------------------------
  309. class Line(_Entity):
  310.     """Line"""
  311.     def __init__(self,points,**common):
  312.         _Entity.__init__(self,**common)
  313.         self.points=points
  314.     def __str__(self):
  315.         return '  0\nLINE\n%s%s\n' %(
  316.                 self._common(), _points(self.points))
  317.  
  318.  
  319. #-----------------------------------------------
  320. class PolyLine(_Entity):
  321.     def __init__(self,points,org_point=[0,0,0],flag=0,width=None,**common):
  322.         #width = number, or width = list [width_start=None, width_end=None]
  323.         #for 2d-polyline: points = [ [x, y, z, width_start=None, width_end=None, bulge=0 or None], ...]
  324.         #for 3d-polyline: points = [ [x, y, z], ...]
  325.         #for polyface: points = [points_list, faces_list]
  326.         _Entity.__init__(self,**common)
  327.         self.points=points
  328.         self.org_point=org_point
  329.         self.flag=flag
  330.         self.polyface = False
  331.         self.polyline2d = False
  332.         self.faces = [] # dummy value
  333.         self.width= None # dummy value
  334.         if self.flag & POLYFACE_MESH:
  335.             self.polyface=True
  336.             self.points=points[0]
  337.             self.faces=points[1]
  338.             self.p_count=len(self.points)
  339.             self.f_count=len(self.faces)
  340.         elif not self.flag & POLYLINE_3D:
  341.             self.polyline2d = True
  342.             if width:
  343.                 if type(width)!='list':
  344.                     width=[width,width]
  345.                 self.width=width
  346.  
  347.     def __str__(self):
  348.         result= '  0\nPOLYLINE\n%s 70\n%s\n' %(self._common(),self.flag)
  349.         result+=' 66\n1\n'
  350.         result+='%s\n' %_point(self.org_point)
  351.         if self.polyface:
  352.             result+=' 71\n%s\n' %self.p_count
  353.             result+=' 72\n%s\n' %self.f_count
  354.         elif self.polyline2d:
  355.             if self.width!=None: result+=' 40\n%s\n 41\n%s\n' %(self.width[0],self.width[1])
  356.         for point in self.points:
  357.             result+='  0\nVERTEX\n'
  358.             result+='  8\n%s\n' %self.layer
  359.             if self.polyface:
  360.                 result+='%s\n' %_point(point[0:3])
  361.                 result+=' 70\n192\n'
  362.             elif self.polyline2d:
  363.                 result+='%s\n' %_point(point[0:2])
  364.                 if len(point)>4:
  365.                     width1, width2 = point[3], point[4]
  366.                     if width1!=None: result+=' 40\n%s\n' %width1
  367.                     if width2!=None: result+=' 41\n%s\n' %width2
  368.                 if len(point)==6:
  369.                     bulge = point[5]
  370.                     if bulge: result+=' 42\n%s\n' %bulge
  371.             else:
  372.                 result+='%s\n' %_point(point[0:3])
  373.         for face in self.faces:
  374.             result+='  0\nVERTEX\n'
  375.             result+='  8\n%s\n' %self.layer
  376.             result+='%s\n' %_point(self.org_point)
  377.             result+=' 70\n128\n'
  378.             result+=' 71\n%s\n' %face[0]
  379.             result+=' 72\n%s\n' %face[1]
  380.             result+=' 73\n%s\n' %face[2]
  381.             if len(face)==4: result+=' 74\n%s\n' %face[3]
  382.         result+='  0\nSEQEND\n'
  383.         result+='  8\n%s\n' %self.layer
  384.         return result
  385.  
  386. #-----------------------------------------------
  387. class Point(_Entity):
  388.     """Point."""
  389.     def __init__(self,points=None,**common):
  390.         _Entity.__init__(self,**common)
  391.         self.points=points
  392.     def __str__(self): # TODO:
  393.         return '  0\nPOINT\n%s%s\n' %(self._common(),
  394.              _points(self.points)
  395.             )
  396.  
  397. #-----------------------------------------------
  398. class Solid(_Entity):
  399.     """Colored solid fill."""
  400.     def __init__(self,points=None,**common):
  401.         _Entity.__init__(self,**common)
  402.         self.points=points
  403.     def __str__(self):
  404.         return '  0\nSOLID\n%s%s\n' %(self._common(),
  405.              _points(self.points[:2]+[self.points[3],self.points[2]])
  406.             )
  407.  
  408.  
  409. #-----------------------------------------------
  410. class Text(_Entity):
  411.     """Single text line."""
  412.     def __init__(self,text='',point=(0,0,0),alignment=None,
  413.                  flag=None,height=1,justifyhor=None,justifyver=None,
  414.                  rotation=None,obliqueAngle=None,style=None,xscale=None,**common):
  415.         _Entity.__init__(self,**common)
  416.         self.text=text
  417.         self.point=point
  418.         self.alignment=alignment
  419.         self.flag=flag
  420.         self.height=height
  421.         self.justifyhor=justifyhor
  422.         self.justifyver=justifyver
  423.         self.rotation=rotation
  424.         self.obliqueAngle=obliqueAngle
  425.         self.style=style
  426.         self.xscale=xscale
  427.     def __str__(self):
  428.         result= '  0\nTEXT\n%s%s\n 40\n%s\n  1\n%s\n'%\
  429.                 (self._common(),_point(self.point),self.height,self.text)
  430.         if self.rotation: result+=' 50\n%s\n'%self.rotation
  431.         if self.xscale: result+=' 41\n%s\n'%self.xscale
  432.         if self.obliqueAngle: result+=' 51\n%s\n'%self.obliqueAngle
  433.         if self.style: result+='  7\n%s\n'%self.style
  434.         if self.flag: result+=' 71\n%s\n'%self.flag
  435.         if self.justifyhor: result+=' 72\n%s\n'%self.justifyhor
  436.         if self.alignment: result+='%s\n'%_point(self.alignment,1)
  437.         if self.justifyver: result+=' 73\n%s\n'%self.justifyver
  438.         return result
  439.  
  440. #-----------------------------------------------
  441. class Mtext(Text):
  442.     """Surrogate for mtext, generates some Text instances."""
  443.     def __init__(self,text='',point=(0,0,0),width=250,spacingFactor=1.5,down=0,spacingWidth=None,**options):
  444.         Text.__init__(self,text=text,point=point,**options)
  445.         if down:spacingFactor*=-1
  446.         self.spacingFactor=spacingFactor
  447.         self.spacingWidth=spacingWidth
  448.         self.width=width
  449.         self.down=down
  450.     def __str__(self):
  451.         texts=self.text.replace('\r\n','\n').split('\n')
  452.         if not self.down:texts.reverse()
  453.         result=''
  454.         x=y=0
  455.         if self.spacingWidth:spacingWidth=self.spacingWidth
  456.         else:spacingWidth=self.height*self.spacingFactor
  457.         for text in texts:
  458.             while text:
  459.                 result+='%s\n'%Text(text[:self.width],
  460.                     point=(self.point[0]+x*spacingWidth,
  461.                            self.point[1]+y*spacingWidth,
  462.                            self.point[2]),
  463.                     alignment=self.alignment,flag=self.flag,height=self.height,
  464.                     justifyhor=self.justifyhor,justifyver=self.justifyver,
  465.                     rotation=self.rotation,obliqueAngle=self.obliqueAngle,
  466.                     style=self.style,xscale=self.xscale,parent=self
  467.                 )
  468.                 text=text[self.width:]
  469.                 if self.rotation:x+=1
  470.                 else:y+=1
  471.         return result[1:]
  472.  
  473. #-----------------------------------------------
  474. ##class _Mtext(_Entity):
  475. ##    """Mtext not functioning for minimal dxf."""
  476. ##    def __init__(self,text='',point=(0,0,0),attachment=1,
  477. ##                 charWidth=None,charHeight=1,direction=1,height=100,rotation=0,
  478. ##                 spacingStyle=None,spacingFactor=None,style=None,width=100,
  479. ##                 xdirection=None,**common):
  480. ##        _Entity.__init__(self,**common)
  481. ##        self.text=text
  482. ##        self.point=point
  483. ##        self.attachment=attachment
  484. ##        self.charWidth=charWidth
  485. ##        self.charHeight=charHeight
  486. ##        self.direction=direction
  487. ##        self.height=height
  488. ##        self.rotation=rotation
  489. ##        self.spacingStyle=spacingStyle
  490. ##        self.spacingFactor=spacingFactor
  491. ##        self.style=style
  492. ##        self.width=width
  493. ##        self.xdirection=xdirection
  494. ##    def __str__(self):
  495. ##        input=self.text
  496. ##        text=''
  497. ##        while len(input)>250:
  498. ##            text+='3\n%s\n'%input[:250]
  499. ##            input=input[250:]
  500. ##        text+='1\n%s\n'%input
  501. ##        result= '0\nMTEXT\n%s\n%s\n40\n%s\n41\n%s\n71\n%s\n72\n%s%s\n43\n%s\n50\n%s\n'%\
  502. ##                (self._common(),_point(self.point),self.charHeight,self.width,
  503. ##                 self.attachment,self.direction,text,
  504. ##                 self.height,
  505. ##                 self.rotation)
  506. ##        if self.style:result+='7\n%s\n'%self.style
  507. ##        if self.xdirection:result+='%s\n'%_point(self.xdirection,1)
  508. ##        if self.charWidth:result+='42\n%s\n'%self.charWidth
  509. ##        if self.spacingStyle:result+='73\n%s\n'%self.spacingStyle
  510. ##        if self.spacingFactor:result+='44\n%s\n'%self.spacingFactor
  511. ##        return result
  512.  
  513. #---tables ---------------------------------------------------
  514. #-----------------------------------------------
  515. class Block(_Collection):
  516.     """Use list methods to add entities, eg append."""
  517.     def __init__(self,name,layer='0',flag=0,base=(0,0,0),entities=[]):
  518.         self.entities=copy.copy(entities)
  519.         _Collection.__init__(self,entities)
  520.         self.layer=layer
  521.         self.name=name
  522.         self.flag=0
  523.         self.base=base
  524.     def __str__(self): # TODO:
  525.         e=''.join([str(x)for x in self.entities])
  526.         return '  0\nBLOCK\n  8\n%s\n  2\n%s\n 70\n%s\n%s\n  3\n%s\n%s  0\nENDBLK\n'%\
  527.                (self.layer,self.name.upper(),self.flag,_point(self.base),self.name.upper(),e)
  528.  
  529. #-----------------------------------------------
  530. class Layer(_Call):
  531.     """Layer"""
  532.     def __init__(self,name='pydxf',color=7,lineType='continuous',flag=64):
  533.         self.name=name
  534.         self.color=color
  535.         self.lineType=lineType
  536.         self.flag=flag
  537.     def __str__(self):
  538.         return '  0\nLAYER\n  2\n%s\n 70\n%s\n 62\n%s\n  6\n%s\n'%\
  539.                (self.name.upper(),self.flag,self.color,self.lineType)
  540.  
  541. #-----------------------------------------------
  542. class LineType(_Call):
  543.     """Custom linetype"""
  544.     def __init__(self,name='CONTINUOUS',description='Solid line',elements=[0.0],flag=0):
  545.         self.name=name
  546.         self.description=description
  547.         self.elements=copy.copy(elements)
  548.         self.flag=flag
  549.     def __str__(self):
  550.         result = '  0\nLTYPE\n  2\n%s\n 70\n%s\n  3\n%s\n 72\n65\n'%\
  551.             (self.name.upper(),self.flag,self.description)
  552.         if self.elements:
  553.             elements = ' 73\n%s\n' %(len(self.elements)-1)
  554.             elements += ' 40\n%s\n' %(self.elements[0])
  555.             for e in self.elements[1:]:
  556.                 elements += ' 49\n%s\n' %e
  557.             result += elements
  558.         return result
  559.          
  560.  
  561. #-----------------------------------------------
  562. class Style(_Call):
  563.     """Text style"""
  564.     def __init__(self,name='standard',flag=0,height=0,widthFactor=1.0,obliqueAngle=0.0,
  565.                  mirror=0,lastHeight=1,font='arial.ttf',bigFont=''):
  566.         self.name=name
  567.         self.flag=flag
  568.         self.height=height
  569.         self.widthFactor=widthFactor
  570.         self.obliqueAngle=obliqueAngle
  571.         self.mirror=mirror
  572.         self.lastHeight=lastHeight
  573.         self.font=font
  574.         self.bigFont=bigFont
  575.     def __str__(self):
  576.         return '  0\nSTYLE\n  2\n%s\n 70\n%s\n 40\n%s\n 41\n%s\n 50\n%s\n 71\n%s\n 42\n%s\n 3\n%s\n 4\n%s\n'%\
  577.                (self.name.upper(),self.flag,self.flag,self.widthFactor,
  578.                 self.obliqueAngle,self.mirror,self.lastHeight,
  579.                 self.font.upper(),self.bigFont.upper())
  580.  
  581. #-----------------------------------------------
  582. class VPort(_Call):
  583.     def __init__(self,name,flag=0,
  584.                 leftBottom=(0.0,0.0),
  585.                 rightTop=(1.0,1.0),
  586.                 center=(0.5,0.5),
  587.                 snap_base=(0.0,0.0),
  588.                 snap_spacing=(0.1,0.1),
  589.                 grid_spacing=(0.1,0.1),
  590.                 direction=(0.0,0.0,1.0),
  591.                 target=(0.0,0.0,0.0),
  592.                 height=1.0,
  593.                 ratio=1.0,
  594.                 lens=50,
  595.                 frontClipping=0,
  596.                 backClipping=0,
  597.                 snap_rotation=0,
  598.                 twist=0,
  599.                 mode=0,
  600.                 circle_zoom=100,
  601.                 fast_zoom=1,
  602.                 ucsicon=1,
  603.                 snap_on=0,
  604.                 grid_on=0,
  605.                 snap_style=0,
  606.                 snap_isopair=0
  607.                 ):
  608.         self.name=name
  609.         self.flag=flag
  610.         self.leftBottom=leftBottom
  611.         self.rightTop=rightTop
  612.         self.center=center
  613.         self.snap_base=snap_base
  614.         self.snap_spacing=snap_spacing
  615.         self.grid_spacing=grid_spacing
  616.         self.direction=direction
  617.         self.target=target
  618.         self.height=float(height)
  619.         self.ratio=float(ratio)
  620.         self.lens=float(lens)
  621.         self.frontClipping=float(frontClipping)
  622.         self.backClipping=float(backClipping)
  623.         self.snap_rotation=float(snap_rotation)
  624.         self.twist=float(twist)
  625.         self.mode=mode
  626.         self.circle_zoom=circle_zoom
  627.         self.fast_zoom=fast_zoom
  628.         self.ucsicon=ucsicon
  629.         self.snap_on=snap_on
  630.         self.grid_on=grid_on
  631.         self.snap_style=snap_style
  632.         self.snap_isopair=snap_isopair
  633.     def __str__(self):
  634.         output = ['  0', 'VPORT',
  635.             '  2', self.name,
  636.             ' 70', self.flag,
  637.             _point(self.leftBottom),
  638.             _point(self.rightTop,1),
  639.             _point(self.center,2), # View center point (in DCS)
  640.             _point(self.snap_base,3),
  641.             _point(self.snap_spacing,4),
  642.             _point(self.grid_spacing,5),
  643.             _point(self.direction,6), #view direction from target (in WCS)
  644.             _point(self.target,7),
  645.             ' 40', self.height,
  646.             ' 41', self.ratio,
  647.             ' 42', self.lens,
  648.             ' 43', self.frontClipping,
  649.             ' 44', self.backClipping,
  650.             ' 50', self.snap_rotation,
  651.             ' 51', self.twist,
  652.             ' 71', self.mode,
  653.             ' 72', self.circle_zoom,
  654.             ' 73', self.fast_zoom,
  655.             ' 74', self.ucsicon,
  656.             ' 75', self.snap_on,
  657.             ' 76', self.grid_on,
  658.             ' 77', self.snap_style,
  659.             ' 78', self.snap_isopair
  660.             ]
  661.  
  662.         output_str = ''
  663.         for s in output:
  664.             output_str += '%s\n' %s
  665.         return output_str
  666.  
  667.  
  668.  
  669. #-----------------------------------------------
  670. class View(_Call):
  671.     def __init__(self,name,flag=0,
  672.             width=1,
  673.             height=1,
  674.             center=(0.5,0.5),
  675.             direction=(0,0,1),
  676.             target=(0,0,0),
  677.             lens=50,
  678.             frontClipping=0,
  679.             backClipping=0,
  680.             twist=0,mode=0
  681.             ):
  682.         self.name=name
  683.         self.flag=flag
  684.         self.width=float(width)
  685.         self.height=float(height)
  686.         self.center=center
  687.         self.direction=direction
  688.         self.target=target
  689.         self.lens=float(lens)
  690.         self.frontClipping=float(frontClipping)
  691.         self.backClipping=float(backClipping)
  692.         self.twist=float(twist)
  693.         self.mode=mode
  694.     def __str__(self):
  695.         output = ['  0', 'VIEW',
  696.             '  2', self.name,
  697.             ' 70', self.flag,
  698.             ' 40', self.height,
  699.             _point(self.center),
  700.             ' 41', self.width,
  701.             _point(self.direction,1),
  702.             _point(self.target,2),
  703.             ' 42', self.lens,
  704.             ' 43', self.frontClipping,
  705.             ' 44', self.backClipping,
  706.             ' 50', self.twist,
  707.             ' 71', self.mode
  708.             ]
  709.         output_str = ''
  710.         for s in output:
  711.             output_str += '%s\n' %s
  712.         return output_str
  713.  
  714. #-----------------------------------------------
  715. def ViewByWindow(name,leftBottom=(0,0),rightTop=(1,1),**options):
  716.     width=abs(rightTop[0]-leftBottom[0])
  717.     height=abs(rightTop[1]-leftBottom[1])
  718.     center=((rightTop[0]+leftBottom[0])*0.5,(rightTop[1]+leftBottom[1])*0.5)
  719.     return View(name=name,width=width,height=height,center=center,**options)
  720.  
  721. #---drawing
  722. #-----------------------------------------------
  723. class Drawing(_Collection):
  724.     """Dxf drawing. Use append or any other list methods to add objects."""
  725.     def __init__(self,insbase=(0.0,0.0,0.0),extmin=(0.0,0.0,0.0),extmax=(0.0,0.0,0.0),
  726.                  layers=[Layer()],linetypes=[LineType()],styles=[Style()],blocks=[],
  727.                  views=[],vports=[],entities=None,fileName='test.dxf'):
  728.         # TODO: replace list with None,arial
  729.         if not entities:
  730.             entities=[]
  731.         _Collection.__init__(self,entities)
  732.         self.insbase=insbase
  733.         self.extmin=extmin
  734.         self.extmax=extmax
  735.         self.layers=copy.copy(layers)
  736.         self.linetypes=copy.copy(linetypes)
  737.         self.styles=copy.copy(styles)
  738.         self.views=copy.copy(views)
  739.         self.vports=copy.copy(vports)
  740.         self.blocks=copy.copy(blocks)
  741.         self.fileName=fileName
  742.         #private
  743.         #self.acadver='9\n$ACADVER\n1\nAC1006\n'
  744.         self.acadver='  9\n$ACADVER\n  1\nAC1009\n'
  745.         """DXF AutoCAD-Release format codes
  746.         AC1021  2008, 2007 
  747.         AC1018  2006, 2005, 2004 
  748.         AC1015  2002, 2000i, 2000 
  749.         AC1014  R14,14.01 
  750.         AC1012  R13    
  751.         AC1009  R12,11 
  752.         AC1006  R10    
  753.         AC1004  R9    
  754.         AC1002  R2.6  
  755.         AC1.50  R2.05 
  756.         """
  757.  
  758.     def _name(self,x):
  759.         """Helper function for self._point"""
  760.         return '  9\n$%s\n' %x.upper()
  761.  
  762.     def _point(self,name,x):
  763.         """Point setting from drawing like extmin,extmax,..."""
  764.         return '%s%s' %(self._name(name),_point(x))
  765.  
  766.     def _section(self,name,x):
  767.         """Sections like tables,blocks,entities,..."""
  768.         if x: xstr=''.join(x)
  769.         else: xstr=''
  770.         return '  0\nSECTION\n  2\n%s\n%s  0\nENDSEC\n'%(name.upper(),xstr)
  771.  
  772.     def _table(self,name,x):
  773.         """Tables like ltype,layer,style,..."""
  774.         if x: xstr=''.join(x)
  775.         else: xstr=''
  776.         return '  0\nTABLE\n  2\n%s\n 70\n%s\n%s  0\nENDTAB\n'%(name.upper(),len(x),xstr)
  777.  
  778.     def __str__(self):
  779.         """Returns drawing as dxf string."""
  780.         header=[self.acadver]+[self._point(attr,getattr(self,attr))+'\n' for attr in _HEADER_POINTS]
  781.         header=self._section('header',header)
  782.  
  783.         tables=[self._table('vport',[str(x) for x in self.vports]),
  784.                 self._table('ltype',[str(x) for x in self.linetypes]),
  785.                 self._table('layer',[str(x) for x in self.layers]),
  786.                 self._table('style',[str(x) for x in self.styles]),
  787.                 self._table('view',[str(x) for x in self.views]),
  788.         ]
  789.         tables=self._section('tables',tables)
  790.  
  791.         blocks=self._section('blocks',[str(x) for x in self.blocks])
  792.  
  793.         entities=self._section('entities',[str(x) for x in self.entities])
  794.  
  795.         all=''.join([header,tables,blocks,entities,'  0\nEOF\n'])
  796.         return all
  797.  
  798.     def saveas(self,fileName):
  799.         self.fileName=fileName
  800.         self.save()
  801.  
  802.     def save(self):
  803.         test=open(self.fileName,'w')
  804.         test.write(str(self))
  805.         test.close()
  806.  
  807.  
  808. #---extras
  809. #-----------------------------------------------
  810. class Rectangle(_Entity):
  811.     """Rectangle, creates lines."""
  812.     def __init__(self,point=(0,0,0),width=1,height=1,solid=None,line=1,**common):
  813.         _Entity.__init__(self,**common)
  814.         self.point=point
  815.         self.width=width
  816.         self.height=height
  817.         self.solid=solid
  818.         self.line=line
  819.     def __str__(self):
  820.         result=''
  821.         points=[self.point,(self.point[0]+self.width,self.point[1],self.point[2]),
  822.             (self.point[0]+self.width,self.point[1]+self.height,self.point[2]),
  823.             (self.point[0],self.point[1]+self.height,self.point[2]),self.point]
  824.         if self.solid:
  825.             result+= Solid(points=points[:-1],parent=self.solid)
  826.         if self.line:
  827.             for i in range(4):
  828.                 result+= Line(points=[points[i],points[i+1]],parent=self)
  829.         return result[1:]
  830.  
  831. #-----------------------------------------------
  832. class LineList(_Entity):
  833.     """Like polyline, but built of individual lines."""
  834.     def __init__(self,points=[],org_point=[0,0,0],closed=0,**common):
  835.         _Entity.__init__(self,**common)
  836.         self.closed=closed
  837.         self.points=copy.copy(points)
  838.     def __str__(self):
  839.         if self.closed:points=self.points+[self.points[0]]
  840.         else: points=self.points
  841.         result=''
  842.         for i in range(len(points)-1):
  843.             result+= Line(points=[points[i],points[i+1]],parent=self)
  844.         return result[1:]
  845.  
  846. #-----------------------------------------------------
  847. def test():
  848.     #Blocks
  849.     b=Block('test')
  850.     b.append(Solid(points=[(0,0,0),(1,0,0),(1,1,0),(0,1,0)],color=1))
  851.     b.append(Arc(center=(1,0,0),color=2))
  852.  
  853.     #Drawing
  854.     d=Drawing()
  855.     #tables
  856.     d.blocks.append(b)  #table blocks
  857.     d.styles.append(Style())  #table styles
  858.     d.views.append(View('Normal'))  #table view
  859.     d.views.append(ViewByWindow('Window',leftBottom=(1,0),rightTop=(2,1)))  #idem
  860.  
  861.     #entities
  862.     d.append(Circle(center=(1,1,0),color=3))
  863.     d.append(Face(points=[(0,0,0),(1,0,0),(1,1,0),(0,1,0)],color=4))
  864.     d.append(Insert('test',point=(3,3,3),cols=5,colspacing=2))
  865.     d.append(Line(points=[(0,0,0),(1,1,1)]))
  866.     d.append(Mtext('Click on Ads\nmultiple lines with mtext',point=(1,1,1),color=5,rotation=90))
  867.     d.append(Text('Please donate!',point=(3,0,1)))
  868.     #d.append(Rectangle(point=(2,2,2),width=4,height=3,color=6,solid=Solid(color=2)))
  869.     d.append(Solid(points=[(4,4,0),(5,4,0),(7,8,0),(9,9,0)],color=3))
  870.     #d.append(PolyLine(points=[(1,1,1),(2,1,1),(2,2,1),(1,2,1)],flag=1,color=1))
  871.  
  872.     #d.saveas('c:\\test.dxf')
  873.     d.saveas('test.dxf')
  874.  
  875. #-----------------------------------------------------
  876. if __name__=='__main__':
  877.     if not copy:
  878.         Draw.PupMenu('Error%t|This script requires a full python install')
  879.     else: test()
  880.